package util.xmlmanager;

import java.io.File;
import java.util.LinkedList;

import util.essential.SourceGetter;
import util.xmlmanager.elements.Attribute;
import util.xmlmanager.elements.Element;
import util.xmlmanager.elements.XMLDocument;

/**
 * @author Byte77.
 * @version 2.0.
 */
public class XMLManager {
	// Constants:
	private final String PROMPT = "XMLManager> ";
	private final String LN = "\n";

	// Attributes:
	private LinkedList<XMLDocument> documentsList;

	// Constructors:
	public XMLManager() {
		this.documentsList = new LinkedList<XMLDocument>();
	}

	// Methods:
	/**
	 * This method allows to add a new XMLDocument with a given XML file name.
	 * 
	 * @param xmlFileName
	 */
	public boolean addXMLDocument(String xmlFileName) {
		if(this.find(xmlFileName)==null){
			this.documentsList.add(XMLBuilder.getXMLRepresentation(xmlFileName));
			return true;
		}else{
			return false;
		}
	}

	/**
	 * This method allows to remove a given XMLDocument.
	 * 
	 * @param xmlFileName
	 * @return whether were possible remove a given XMLDocument.
	 */
	public boolean removeXMLDocument(String xmlFileName) {
		XMLDocument document = this.find(xmlFileName);
		if (document != null) {
			System.out.println("Removing...");
			this.documentsList.remove(document);
			return true;
		}
		return false;
	}

	/**
	 * @param path
	 * @return selected elements.
	 */
	public LinkedList<Element> select(String path) {
		return this.select(path, null);
	}

	/**
	 * @param path
	 * @param constraints
	 * @return selected elements.
	 */
	public LinkedList<Element> select(String path,
			LinkedList<Attribute> constraints) {
		LinkedList<String> tmp = new LinkedList<String>();
		tmp.add(path);
		return this.getSelection(tmp, constraints);
	}

	public String getAttributeValue(String path, String attributeName){
		LinkedList<Element> elements = this.select(path);
		if(elements!=null && elements.size() == 1){
			return elements.getFirst().getAttribute(attributeName).getValue();
		}else{
			return null;
		}
	}
	
	/**
	 * This method allows to insert a new element to a specified path over all
	 * of files.
	 * 
	 * @param path
	 * @param element
	 * @param constraints
	 * @return whether element could be inserted.
	 * 
	 *         TODO: Save document.
	 */
	public boolean addElement(String path, Element element,
			LinkedList<Attribute> constraints) {
		LinkedList<String> pathList = new LinkedList<String>();
		pathList.add(path);
		LinkedList<Element> tmp = this.getSelection(pathList, constraints);
		if (tmp != null && !tmp.isEmpty()) {
			for (Element elementTmp : tmp) {
				elementTmp.addElement(element);
			}
			return true;
		} else {
			return false;
		}
	}

	/**
	 * This method allows to insert a new element to a specified file.
	 * 
	 * @param file
	 * @param path
	 * @param element
	 * @param constraints
	 * @return whether element could be inserted.
	 * 
	 *         TODO: Save document.
	 */
	public boolean addElement(File file, String path, Element element,
			LinkedList<Attribute> constraints) {
		XMLDocument document = this.find(file.getName());
		if (document != null) {
			LinkedList<Element> tmp = document.getElement().find(
					this.getPath(path), constraints);
			for (Element elementTmp : tmp) {
				elementTmp.addElement(element);
			}
			return true;
		} else {
			return false;
		}
	}

	// private boolean saveDocument(String fileName){
	// //TODO ...
	// return false;
	// }

	/**
	 * This method allows to get a given document under the name of xmlFileName.
	 * 
	 * @param xmlFileName
	 * @return XMLDocument with xmlFileName file name.
	 */
	private XMLDocument find(String xmlFileName) {
		for (XMLDocument document : this.documentsList) {
			if (document.getXmlFileName().equals(xmlFileName)) {
				return document;
			}
		}
		return null;
	}

	/**
	 * This method allows to get a selection of elements.
	 * 
	 * @param elementsList
	 * @return list of selected elements.
	 */
	private LinkedList<Element> getSelection(LinkedList<String> elementsList,
			LinkedList<Attribute> constraints) {// TODO ...
		LinkedList<Element> ans = new LinkedList<Element>(), tmp;
		for (XMLDocument document : this.documentsList) {
			for (String source : elementsList) {
				SourceGetter.out.debug(this.PROMPT + "[getSelection] ",
						"----------------------------------");
				SourceGetter.out.debug(this.PROMPT + "[getSelection] ",
						"Element> " + source + "; No. of XMLDocs: "
								+ this.documentsList.size());
				SourceGetter.out.debug(this.PROMPT + "[getSelection] ",
						"Path> " + this.getPath(source));
				tmp = document.getElement().find(this.getPath(source),
						constraints);
				if (tmp != null) {
					ans.addAll(tmp);
					SourceGetter.out.debug(this.PROMPT + "[getSelection] ",
							"Found path and added--> " + tmp.toString());
				}/**/else {
					SourceGetter.out.debug(this.PROMPT + "[getSelection] ",
							"Path not found!!!");
				}/**/
			}
		}
		return ans;
	}

	// public LinkedList<Attribute> getAttributes(String path){
	// LinkedList<Attribute> ans = new LinkedList<Attribute>();
	// LinkedList<Element> tmp = new LinkedList<Element>();
	// for (XMLDocument document : this.documentsList) {
	// tmp = document.getElement().find(this.getPath(path));
	// if(tmp!=null){
	// for (Element element : tmp) {
	// ans.addAll(element.getAttributes());
	// }
	// }/**/else{
	// SourceGetter.out.debug(this.PROMPT+"[getAttributes] ","None attributes!!!");
	// }/**/
	// }
	// return ans;
	// }

	/**
	 * @param path
	 * @return a linked list representation of a given path.
	 */
	private LinkedList<String> getPath(String path) {
		LinkedList<String> ans = new LinkedList<String>();
		String[] tmp = path.split("[.]");
		for (String string : tmp) {
			ans.add(string);
		}
		return ans;
	}
	
	public String toString(){
		StringBuffer buff = new StringBuffer();
		for (XMLDocument document : this.documentsList) {
			buff.append(document.toString()+this.LN);
		}
		return buff.toString();
	}
	public String toString(String fileName){
		StringBuffer buff = new StringBuffer();
		XMLDocument document = this.find(fileName);
		if(document!=null){
			buff.append(document.toString()+this.LN);
		}else{
			buff.append("NULL!!!"+this.LN);
		}
		return buff.toString();
	}
}
